/* See COPYRIGHT for copyright information. */
#include <inc/boot1.h>
#include <inc/stdio.h>
#include <inc/string.h>
#include <inc/assert.h>
#include <inc/mmap.h>
#include <inc/mp.h>
#include <inc/irq.h>
#include <inc/apic.h>
#include <inc/acpi.h>
#include <inc/x86.h>
#include <inc/cpu.h>
#include <kern/monitor.h>
#include <kern/console.h>
#include <kern/multiboot1.h>
#include <inc/linux-header.h>
#include <boot/bootparam.h>
#include <inc/svm.h>
// Test the stack backtrace function (lab 1 only)

struct sysx_info sys_io;
struct multiboot_header *mbh;
struct multiboot_info *mbinf;
memory_map_t *mem_map;
module_t *mdul;
struct boot_params *boot_params;


volatile uint32_t ap_svm = 0;

void linux_bootparams(struct boot_params *boot_params)
{
    cprintf("edd entries is %d\n", boot_params->eddbuf_entries);
    cprintf("vid mode is %d\n", boot_params->hdr.vid_mode);
}

void mboot_parse(struct multiboot_info *mf)
{
    mem_map = (memory_map_t *)((uint64_t) mf->mmap_addr);
    mdul = (module_t *)((uint64_t) mf->mods_addr);
    sys_io.cmdline = mf->cmdline;
    sys_io.e820_nents = 0;
    cprintf("size	addr	 	type\n"); 
    for(;(uint64_t) mem_map < mbinf->mmap_addr + mbinf->mmap_length;
	mem_map = (memory_map_t *)((uint64_t)mem_map + mem_map->size + sizeof(mem_map->size)))
    {
	sys_io.e820_map[sys_io.e820_nents].addr = (uint64_t)mem_map->base_addr_low
	    + (((uint64_t) mem_map->base_addr_high)<<32); 
	sys_io.e820_map[sys_io.e820_nents].size = (uint64_t)mem_map->length_low +
	    (((uint64_t) mem_map->length_high)<<32);
	sys_io.e820_map[sys_io.e820_nents].type = mem_map->type;
	cprintf("0x%lx,					0x%lx , type is %d\n",
		sys_io.e820_map[sys_io.e820_nents].addr,sys_io.e820_map[sys_io.e820_nents].size,
		sys_io.e820_map[sys_io.e820_nents].type); 
	sys_io.e820_nents ++;
    }
}

void
test_backtrace(int x)
{
    cprintf("entering test_backtrace %d\n", x);
    if (x > 0)
	test_backtrace(x-1);
    else
	mon_backtrace(0, 0, 0);
    cprintf("leaving test_backtrace %d\n", x);
}

void ap_init()
{
    *AP_BOOT_SIG = 0xdcba;
    delay(30000000);
    lock_cprintf("hello world , I'm ap 0x%x\n", lapicid());
    load_idt();
    enable_lapic();
    cpus[lapicid() - 4].booted = 1;
    lock_cprintf("cpu %d is booed %d\n", lapicid(), cpus[lapicid() - 4].booted);
	
    //	__asm __volatile("sti");
    while(ap_svm == 0)
	__asm__ __volatile__("pause"::);
    start_svm();
}


void
i386_init(uint32_t edi, uint32_t esi, uint32_t rdx, uint32_t ecx)
{
    extern char edata[], end[];
    uint32_t eax, ebx, epx, edx;
    

    moveself_to_last_Gb(last_GB_addr);
	

    // Before doing anything else, complete the ELF loading process.
    // Clear the uninitialized global data (BSS) section of our program.
    // This ensures that all static/global variables start out zero.
    memset(edata, 0, end - edata);

    cons_init();

    cprintf("edi 0x%x, esi 0x%x, rdx 0x%x, ecx 0x%x\n", edi, esi, rdx, ecx);
    boot_params = (struct boot_params *)((uint64_t)edi);
    char *cmdline = "root=UUID=086d3ba1-0f93-4008-95ce-a2f90a8453a3 ro quiet splash\0";
	
    memcpy((char *)0x9800, cmdline, 256);

	
    if(rdx == 0x2badb002)
    {
	cprintf("multiboot ok\n");

	mbinf = (struct multiboot_info *)((uint64_t)  ecx);
	cprintf("total mode conts is %d, the mode addr is 0x%x\n",
		mbinf->mods_count, mbinf->mods_addr);
	mboot_parse(mbinf);
    }
    else
    {
	struct sysx_info *tmp;
	tmp = (struct sysx_info *)((uint64_t) rdx);
	memcpy(&sys_io, tmp, sizeof(sys_io));
    }
	
    struct e820entry *entr;
    entr = sys_io.e820_map;
		

    struct rsdp *rsdp;
    rsdp = rsdp_get();

    struct srat *srat = 0;
    srat = srat_get((struct xsdt *) rsdp->xsdt_addr);
	
    struct ivrs *ivrs = 0;
    ivrs = ivrs_get((struct xsdt *) rsdp->xsdt_addr);
	
	
	
    if(rsdp)
    {
	cprintf("rsdp sig:");
	for(uint32_t i = 0; i < 8; i ++)
	{
	    cprintf("%c", rsdp->sig[i]);
	}
	cprintf("\n");
    }
	

    init_8259A();
    set_exception();
    set_interrupt();
    load_idt();

    mp_init();
	
    //pit_init();
    //enable_irq(32, 4);
    enable_irq(33, 4);
    //enable_irq(34, 4);
    //init_rtc();
	
    cpuid(0x80000001, &eax, &ebx, &epx, &edx);
    if(epx & 0x4)
	cprintf("SVM feature is contained in the CPU\n");

    if(read_msr(0xc0010114) & 0x10)
	cprintf("SVM is disabled in BIOS\n");

    if(read_msr(0xc0000080) & 0x1000)
	cprintf("SVM enabled \n");
    else
	cprintf("SVM doesn't exists \n");
	
	
    mdul = (module_t *)((uint64_t) mbinf->mods_addr);

    if(ivrs)
    {
	cprintf("ivrs sig:");
	for(uint32_t i = 0; i < 4; i ++)
	{
	    cprintf("%c", ivrs->header.sig[i]);
	}
	cprintf("\n");
    }
    else 
	cprintf("ivrs no found \n");
	
	
    srat_parse(srat);

    extern struct numa_node node[8];
    for(uint32_t i = 0; i < 8; i ++)
    {
	cprintf("domain %d , cpuid is", i);
	for(uint32_t j = 0; j < node[i].index; j ++)
	{
	    cprintf(" %d,", node[i].lapicid[j]);
	}
	cprintf(" mem start is 0x%lx, size is 0x%lx\n", node[i].base_addr, node[i].length);
    }
    //	__asm __volatile("sti");
    cprintf("here\n");	
	
    //while(1);
	
    //	uint64_t initrd_addr = boot_params->hdr.initrd_addr_max
    //		- mdul[1].mode_end + mdul[1].mode_start;
    //	initrd_addr &= 0xfffff000;
    memmove((void *)0x30000000, (void *)((uint64_t)mdul[1].mode_start),
	    (mdul[1].mode_end - mdul[1].mode_start + 1)); 
    linux_move((void *)((uint64_t) mdul->mode_start), mdul->mode_end -
	       mdul->mode_start + 1); 

    cprintf("test\n");
    struct lheader *lh = (struct lheader *)0x90000;
    cprintf("vidmode in lheader is %d\n", lh->vid_mode);
    memcpy(&boot_params->hdr, &lh->setup_sectors, sizeof(boot_params->hdr));

    //lh->vid_mode = 3840;
    if(lh)
    {
	cprintf("Linux sig found :");
	for(uint32_t i = 0; i < 4; i ++)
	    cprintf("%c", lh->linux_sig[i]);
	cprintf("\n");
    }
    boot_params->hdr = *((struct setup_header *) &(lh->setup_sectors));
	
	
    boot_params->hdr.cmd_line_ptr = 0x9800;
    cprintf("kernel relocable is 0x%x, alignment address is 0x%x\n" 
	    ,boot_params->hdr.relocatable_kernel,
	    boot_params->hdr.kernel_alignment); 
    cprintf("initrd mode start addr is 0x%x, initrd end at 0x%x\n",
	    mdul[1].mode_start, mdul[1].mode_end); 
    boot_params->hdr.ramdisk_image = 0x30000000;
    boot_params->hdr.ramdisk_size = mdul[1].mode_end - mdul[1].mode_start + 1; 
    boot_params->e820_entries = sys_io.e820_nents;
    for(uint32_t i = 0; i < sys_io.e820_nents; i ++)
    {
	boot_params->e820_map[i].addr = sys_io.e820_map[i].addr;
	boot_params->e820_map[i].size = sys_io.e820_map[i].size;
	boot_params->e820_map[i].type = sys_io.e820_map[i].type;
    }	
    cprintf("total e820 map is %d\n", sys_io.e820_nents);

    
    
    for(uint32_t i = 0, j = 0; i < sys_io.e820_nents; i ++)
    {
	if(sys_io.e820_map[i].type == 1)
	{
	    if(j == 1)
	    {
		boot_params->e820_map[i].addr = 0xa00000;
		boot_params->e820_map[i].size -= 0x900000;
	    }
	    //   if(j == 2)
	    //boot_params->e820_map[i].size = 0x140000000;
	    j ++;
	}
    }
    

    //	cprintf("edi :0x%x, esi :0x%x, edx :0x%x, ecx :0x%x\n", edi, esi, rdx, ecx);

	
    //	cprintf("sti over\n");


	
    /*__asm __volatile(
      "sti \n\t"
      "movq $0x700000, %%rax \n\t"
      "vmsave \n\t"::);*/
    linux_bootparams(boot_params);

    //ap_svm = 1;
    //while(1);
    start_svm();	



    // Test the stack backtrace function (lab 1 only)
    //	test_backtrace(5);

    // Drop into the kernel monitor.
	
    while (1);
    monitor(NULL);
}


/*
 * Variable panicstr contains argument to first call to panic; used as flag
 * to indicate that the kernel has already called panic.
 */
static const char *panicstr;

/*
 * Panic is called on unresolvable fatal errors.
 * It prints "panic: mesg", and then enters the kernel monitor.
 */
void
_panic(const char *file, int line, const char *fmt,...)
{
    va_list ap;

    if (panicstr)
	goto dead;
    panicstr = fmt;

    va_start(ap, fmt);
    cprintf("kernel panic at %s:%d: ", file, line);
    vcprintf(fmt, ap);
    cprintf("\n");
    va_end(ap);

 dead:
    /* break into the kernel monitor */
    while (1)
	monitor(NULL);
}

/* like panic, but don't */
void
_warn(const char *file, int line, const char *fmt,...)
{
    va_list ap;

    va_start(ap, fmt);
    cprintf("kernel warning at %s:%d: ", file, line);
    vcprintf(fmt, ap);
    cprintf("\n");
    va_end(ap);
}
